#include "cperhandler.hpp"

#include <fmt/format.h>

#include <ipmid/api-types.hpp>
#include <ipmid/api.hpp>
#include <ipmid/handler.hpp>
#include <phosphor-logging/log.hpp>
#include <sdbusplus/asio/connection.hpp>
#include <sdeventplus/source/time.hpp>

#include <chrono>
#include <filesystem>
#include <fstream>
#include <string>

extern "C"
{
#include <cper-parse.h>
#include <json-c/json.h>
#include <phytium-cper-parse.h>
}

#include <time.h>

namespace fs = std::filesystem;

const std::map<uint8_t, std::string> ipmiSensorPathMap{
    {PROCESSOR, ProcessorInventoryPath},
    {MEMORY, MemoryInventoryPath},
    {PCIE, PCIeIventoryPath}};

const std::map<uint8_t, std::string> ipmiSensorIntfMap{
    {PROCESSOR, "xyz.openbmc_project.Inventory.RASInformation.Processor"},
    {MEMORY, "xyz.openbmc_project.Inventory.RASInformation.Memory"},
    {PCIE, "xyz.openbmc_project.Inventory.RASInformation.PCIe"}};

const std::map<uint8_t, std::string> rasSeverityMap{
    {RECOVERABLE, "Recoverable"},
    {FATAL, "Fatal"},
    {CORRECTED, "Corrected"},
    {INFORMATIONAL, "Informational"}};

const std::map<uint8_t, std::string> redfishSeverityMap{
    {RECOVERABLE, "Warning"},
    {FATAL, "Critical"},
    {CORRECTED, "OK"},
    {INFORMATIONAL, "OK"}};

const std::map<uint8_t, uint8_t> ipmiSelSeverityMap{
    {RECOVERABLE, 1},    // transition to Non-Critical from OK
    {FATAL, 6},          // transition to Non-recoverable
    {CORRECTED, 0},      // transition to OK
    {INFORMATIONAL, 8}}; // Informational

// The reserved memory size for each Die of the ps23 is 16TB
constexpr uint16_t dieMemorySize = 16 * 1024; // GB
// The ps23 has 2 memory channels per Die
constexpr uint8_t dieMemoryCount = 2;

constexpr uint8_t cpuType = PS23_TX64;
// cpu tpye, channel id, dimm id
const std::map<uint8_t, uint8_t> ps23tx16MemoryInfoMap{
{CH0, DIMM1}, {CH1, DIMM0}};

const std::map<uint8_t, uint8_t> ps23tx32MemoryInfoMap{
{CH0, DIMM0}, {CH1, DIMM1}, {CH2, DIMM2}, {CH3, DIMM3}};

const std::map<uint8_t, uint8_t> ps23tx64MemoryInfoMap{
{CH0, DIMM1},{CH1, DIMM0},{CH2, DIMM6},{CH3, DIMM7},
{CH4, DIMM2},{CH5, DIMM3},{CH6, DIMM5},{CH7, DIMM4}};

const std::map<uint8_t, uint8_t>& dieMemoryInfoMap = ps23tx64MemoryInfoMap;

static const char* aerCorrectableErrorString[] = {
    "[RxErr] ", /* Bit Position 0 */
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    "[BadTLP] ",   /* Bit Position 6 */
    "[BadDLLP] ",  /* Bit Position 7 */
    "[Rollover] ", /* Bit Position 8 */
    NULL,
    NULL,
    NULL,
    "[Timeout] ",     /* Bit Position 12 */
    "[NonFatalErr] ", /* Bit Position 13 */
    "[CorrIntErr] ",  /* Bit Position 14 */
    "[HeaderOF] ",    /* Bit Position 15 */
};

static const char* aerUncorrectableErrorString[] = {
    "[Undefined] ", /* Bit Position 0 */
    NULL,
    NULL,
    NULL,
    "[DLP] ",  /* Bit Position 4 */
    "[SDES] ", /* Bit Position 5 */
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    "[TLP] ",              /* Bit Position 12 */
    "[FCP] ",              /* Bit Position 13 */
    "[CmpltTO] ",          /* Bit Position 14 */
    "[CmpltAbrt] ",        /* Bit Position 15 */
    "[UnxCmplt] ",         /* Bit Position 16 */
    "[RxOF] ",             /* Bit Position 17 */
    "[MalfTLP] ",          /* Bit Position 18 */
    "[ECRC] ",             /* Bit Position 19 */
    "[UnsupReq] ",         /* Bit Position 20 */
    "[ACSViol] ",          /* Bit Position 21 */
    "[UncorrIntErr] ",     /* Bit Position 22 */
    "[BlockedTLP] ",       /* Bit Position 23 */
    "[AtomicOpBlocked] ",  /* Bit Position 24 */
    "[TLPBlockedErr] ",    /* Bit Position 25 */
    "[PoisonTLPBlocked] ", /* Bit Position 26 */
};

const char* getAerErrorTypeString(uint8_t severity, uint32_t status)
{
    const char** strings;
    bool haveRsvdZ = false;
    static char strAer[256];
    char strRsvdZ[64] = "[ReservedAndZeroBits:";
    size_t pos = 0;
    memset(strAer, 0, sizeof(strAer));
    size_t stringsSize;

    if (severity == CORRECTED)
    {
        strings = aerCorrectableErrorString;
        stringsSize = sizeof(aerCorrectableErrorString) /
                      sizeof(aerCorrectableErrorString[0]);
    }
    else
    {
        strings = aerUncorrectableErrorString;
        stringsSize = sizeof(aerUncorrectableErrorString) /
                      sizeof(aerUncorrectableErrorString[0]);
    }

    for (int i = 0; i < stringsSize; i++)
    {
        if ((status >> i) & 0x1)
        {
            if (strings[i] == nullptr)
            {
                char tmp[8];
                sprintf(tmp, "%d,", i);
                strncat(strRsvdZ, tmp, sizeof(strRsvdZ) - 1);
                haveRsvdZ = true;
                continue;
            }

            pos += strlen(strings[i]);
            if (pos > sizeof(strAer))
            {
                return strAer;
            }
            strncat(strAer, strings[i], sizeof(strAer) - 1);
        }
    }

    if (haveRsvdZ)
    {
        // Replace ',' at the end of the string with ']'
        strRsvdZ[strlen(strRsvdZ) - 1] = ']';
        if (pos + strlen(strRsvdZ) < sizeof(strAer))
        {
            strncat(strAer, strRsvdZ, sizeof(strAer) - 1);
        }
    }
    return strAer;
}

static void printBuf(const std::string& title, const std::vector<uint8_t>& pBuf)
{
    std::cout << title << std::endl;

    for (size_t i = 0; i < pBuf.size(); i++)
    {
        if (i % 16 == 0)
            std::cout << std::endl;

        fprintf(stderr, "0x%02x ", pBuf[i]);
    }
    std::cout << std::endl;
    return;
}

uint16_t calCrc16(const uint8_t* buf, uint32_t len)
{
    uint16_t kpoly = 0x1021;
    uint16_t kLeftBit = 0x8000;
    uint16_t kExtraRounds = 2;

    uint16_t crc = 0xffff;

    if (buf == nullptr)
    {
        return 0;
    }

    for (int i = 0; i < len + kExtraRounds; ++i)
    {
        for (int j = 0; j < 8; ++j)
        {
            uint16_t xorFlag = (crc & kLeftBit) ? 1 : 0;
            crc = crc << 1;
            if (i < len && (buf[i] & (1 << (7 - j))))
            {
                crc++;
            }
            if (xorFlag)
            {
                crc ^= kpoly;
            }
        }
    }

    return crc;
}

static std::string getUniqueEntryID(std::string prefix)
{
    static time_t prevTs = 0;
    static int index = 0;

    // Get the entry timestamp
    std::time_t curTs = time(0);
    std::tm timeStruct = *std::localtime(&curTs);
    char buf[80];
    // If the timestamp isn't unique, increment the index
    if (curTs == prevTs)
    {
        index++;
    }
    else
    {
        // Otherwise, reset it
        index = 0;
    }
    // Save the timestamp
    prevTs = curTs;
    strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S.00", &timeStruct);
    std::string uniqueId(buf);
    uniqueId = prefix + uniqueId + std::to_string(index);
    return uniqueId;
}

static void addIpmiSelLog(std::string sensorPath,
                          std::vector<uint8_t>& eventData)
{
    bool assert = true;
    std::string message = "IPMI SEL RAS Event";
    uint16_t generatorID = 0x0020;

    printf("[Generate SEL events]:event data 1-3 = 0x%02x 0x%02x 0x%02x\n",
           eventData[0], eventData[1], eventData[2]);

    auto bus = getSdBus();
    try
    {
        auto method = bus->new_method_call("xyz.openbmc_project.Logging.IPMI",
                                           "/xyz/openbmc_project/Logging/IPMI",
                                           "xyz.openbmc_project.Logging.IPMI",
                                           "IpmiSelAdd");
        method.append(message, sensorPath, eventData, assert, generatorID);

        auto selReply = bus->call(method);
        if (selReply.is_method_error())
        {
            std::cerr << "add SEL log error\n";
        }
    }
    catch (const std::exception& e)
    {
        std::cerr << "call SEL log error: " << e.what() << "\n";
    }
    return;
}

static void addRedfishLog(std::string& primaryLogId, std::string& message,
                          std::string& messageArgs,
                          std::string& redfishSeverity,
                          std::string& cperTimestamp)
{
    auto bus = getSdBus();
    std::map<std::string, std::variant<std::string, uint64_t>> params;

    params["Type"] = "CPER";
    params["PrimaryLogId"] = primaryLogId; // file name: cper-log-id(time)
    params["Severity"] = redfishSeverity;
    params["Message"] = message;
    params["MessageArgs"] = messageArgs;
    params["CperTimestamp"] = cperTimestamp + "+00:00";

    printf("[Generate Redfish events]:redfishSeverity = %s\n"
           "Message: %s\nMessageArgs: %s\n",
           redfishSeverity.c_str(), message.c_str(), messageArgs.c_str());

    try
    {
        auto method = bus->new_method_call("xyz.openbmc_project.Dump.Manager",
                                           "/xyz/openbmc_project/dump/faultlog",
                                           "xyz.openbmc_project.Dump.Create",
                                           "CreateDump");
        method.append(params);

        auto response = bus->call(method);
        if (response.is_method_error())
        {
            std::cerr << "createDump error\n";
        }
    }
    catch (const std::exception& e)
    {
        std::cerr << "call createDump error: " << e.what() << "\n";
    }

    return;
}

static uint8_t memoryTotalNumber = 0;
static struct OemMemoryInfo oemMemoryInfoMap[MemoryBufCount];
static size_t mdrSMBIOSSize = 0;

static inline uint8_t* smbiosNextPtr(uint8_t* smbiosDataIn)
{
    if (smbiosDataIn == nullptr)
    {
        return nullptr;
    }
    uint8_t* smbiosData = smbiosDataIn + *(smbiosDataIn + 1);
    int len = 0;
    while ((*smbiosData | *(smbiosData + 1)) != 0)
    {
        smbiosData++;
        len++;
        if (len >= mdrSMBIOSSize) // To avoid endless loop
        {
            return nullptr;
        }
    }
    return smbiosData + separateLen;
}

static inline uint8_t* getSMBIOSTypePtr(uint8_t* smbiosDataIn, uint8_t typeId,
                                        size_t size = 0)
{
    if (smbiosDataIn == nullptr)
    {
        return nullptr;
    }
    char* smbiosData = reinterpret_cast<char*>(smbiosDataIn);
    while ((*smbiosData != '\0') || (*(smbiosData + 1) != '\0'))
    {
        uint32_t len = *(smbiosData + 1);
        if (*smbiosData != typeId)
        {
            smbiosData += len;
            while ((*smbiosData != '\0') || (*(smbiosData + 1) != '\0'))
            {
                smbiosData++;
                len++;
                if (len >= mdrSMBIOSSize) // To avoid endless loop
                {
                    return nullptr;
                }
            }
            smbiosData += separateLen;
            continue;
        }
        if (len < size)
        {
            std::cerr << "Record size mismatch!" << std::endl;
            return nullptr;
        }
        return reinterpret_cast<uint8_t*>(smbiosData);
    }
    return nullptr;
}

static int positionToString(uint8_t positionNum, char* result,
                            uint8_t structLen, uint8_t* dataIn)
{
    if (dataIn == nullptr)
    {
        return -1;
    }
    uint16_t limit = mdrSMBIOSSize; // set a limit to avoid endless loop

    char* target = reinterpret_cast<char*>(dataIn + structLen);
    for (uint8_t index = 1; index < positionNum; index++)
    {
        for (; *target != '\0'; target++)
        {
            limit--;
            // When target = dataIn + structLen + limit,
            // following target++ will be nullptr
            if (limit < 1 || target == nullptr)
            {
                return -1;
            }
        }
        target++;
        if (target == nullptr || *target == '\0')
        {
            return -1; // 0x00 0x00 means end of the entry.
        }
    }
    strncpy(result, target, deviceLocatorBufSize);
    return 0;
}

// Sorted according to the value of smbios handle
int sortOemMemoryInfoMap()
{
    int i, j;

    if (memoryTotalNumber <= 1)
    {
        return 0;
    }

    for (i = 1; i < memoryTotalNumber; i++)
    {
        for (j = 0; j < memoryTotalNumber - i; j++)
        {
            if (oemMemoryInfoMap[j].handle > oemMemoryInfoMap[j + 1].handle)
            {
                struct OemMemoryInfo tmpInfo = oemMemoryInfoMap[j];
                oemMemoryInfoMap[j] = oemMemoryInfoMap[j + 1];
                oemMemoryInfoMap[j + 1] = tmpInfo;
            }
        }
    }

    // Note:
    // Motherboard Manufacturer Design:
    // SMBIOS handle value from low to high is the memory channel number.
    for (int i = 0; i < memoryTotalNumber; i++)
    {
        oemMemoryInfoMap[i].channel = i;
    }
    return 0;
}

bool isModifyFile(FILE* smbiosFile)
{
    static long int oldModifyTime = 0;
    struct stat fileStat;

    int fd = fileno(smbiosFile);
    fstat(fd, &fileStat);

    if (fileStat.st_mtime != oldModifyTime)
    {
        oldModifyTime = fileStat.st_mtime;
        return true;
    }
    return false;
}

int syncOemMemoryInfoBySMBIOS(bool skipCheckFileModify)
{
    FILE* smbiosFile = fopen(SMBIOS_FILE_PATH, "r");
    if (smbiosFile == nullptr)
    {
        std::cerr << "Could not open provided smbios file " << SMBIOS_FILE_PATH
                  << ", file handle returned null." << std::endl;
        return -1;
    }

    if (!skipCheckFileModify && !isModifyFile(smbiosFile))
    {
        return memoryTotalNumber;
    }

    fseek(smbiosFile, 0, SEEK_END);
    mdrSMBIOSSize = ftell(smbiosFile);
    fseek(smbiosFile, 0, SEEK_SET);

    uint8_t* storage = (uint8_t*)malloc(mdrSMBIOSSize);
    if (!storage)
    {
        return -1;
    }
    int size = fread(storage, 1, mdrSMBIOSSize, smbiosFile);
    fclose(smbiosFile);
    if (size < 0)
    {
        free(storage);
        return -1;
    }
    uint8_t index = 0;
    uint8_t* dataIn = storage + sizeof(struct MDRSMBIOSHeader);
    do
    {
        dataIn = getSMBIOSTypePtr(dataIn, memoryDeviceType);
        if (dataIn == nullptr || index >= MemoryBufCount)
        {
            break;
        }

        auto memoryInfo = reinterpret_cast<MemoryInfo*>(dataIn);
        oemMemoryInfoMap[index].handle = memoryInfo->handle;

        int ret = positionToString(memoryInfo->deviceLocator,
                                   oemMemoryInfoMap[index].deviceLocator,
                                   memoryInfo->length, dataIn);
        if (ret < 0)
        {
            strcpy(oemMemoryInfoMap[index].deviceLocator, "DIMM X");
        }

        if (memoryInfo->size == 0x7fff)
        {
            oemMemoryInfoMap[index].memorySize =
                memoryInfo->extendedSize * 1024;
        }
        else
        {
            oemMemoryInfoMap[index].memorySize = memoryInfo->size & 0x7fff;
            if (0 == (size & 0x8000))
            {
                oemMemoryInfoMap[index].memorySize *= 1024;
            }
        }

        ++index;

    } while ((dataIn = smbiosNextPtr(dataIn)) != nullptr);

    free(storage);

    memoryTotalNumber = index;
    sortOemMemoryInfoMap();

    for (int i = 0; i < memoryTotalNumber; i++)
    {
        std::cout << fmt::format("[{}]:channel = {},handle = 0x{:x},Size = {} "
                                 "KB,dimmName = {}",
                                 i, oemMemoryInfoMap[i].channel,
                                 oemMemoryInfoMap[i].handle,
                                 oemMemoryInfoMap[i].memorySize,
                                 oemMemoryInfoMap[i].deviceLocator)
                         .c_str()
                  << std::endl;
    }
    return 0;
}

const struct OemMemoryInfo* getMemoryInfoByHandle(uint16_t handle)
{
    syncOemMemoryInfoBySMBIOS(false);

    for (int i = 0; i < memoryTotalNumber; i++)
    {
        if (oemMemoryInfoMap[i].handle == handle)
        {
            return &oemMemoryInfoMap[i];
        }
    }
    return nullptr;
}

inline int
    getRasInformationProperties(const char* path, const char* interface,
                                std::map<std::string, rasValue>& properties)
{
    std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();

    sdbusplus::message::message method =
        dbus->new_method_call("xyz.openbmc_project.Settings", path,
                              "org.freedesktop.DBus.Properties", "GetAll");
    method.append(interface);

    try
    {
        sdbusplus::message::message reply = dbus->call(method);

        reply.read(properties);
    }
    catch (const sdbusplus::exception::exception& e)
    {
        std::cerr << "Error in getting RasInformation Properties: PATH= "
                  << path << ",INTF=" << interface << ",WHAT=" << e.what()
                  << "\n";
    }

    return 0;
}

inline int setRasInformationProperties(const char* path, const char* interface,
                                       const char* properties, rasValue& Value)
{
    std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();

    sdbusplus::message::message method =
        dbus->new_method_call("xyz.openbmc_project.Settings", path,
                              "org.freedesktop.DBus.Properties", "Set");
    method.append(interface, properties, Value);

    try
    {
        dbus->call_noreply(method);
    }
    catch (const sdbusplus::exception::exception& e)
    {
        std::cerr << "Error in Setting RasInformation Properties: PATH= "
                  << path << ",INTF=" << interface << ",WHAT=" << e.what()
                  << "\n";
    }

    return 0;
}

int updateDiscreteSensorSeverity(uint8_t sensorType, uint8_t severity)
{
    std::string sensorSeverity =
        "xyz.openbmc_project.Inventory.RASInformation.Level." +
        rasSeverityMap.at(severity);

    auto bus = getSdBus();
    try
    {
        auto method =
            bus->new_method_call("xyz.openbmc_project.Settings",
                                 ipmiSensorPathMap.at(sensorType).c_str(),
                                 "org.freedesktop.DBus.Properties", "Set");

        method.append("xyz.openbmc_project.Inventory.RASInformation",
                      "Severity", std::variant<std::string>(sensorSeverity));

        auto selReply = bus->call(method);
        if (selReply.is_method_error())
        {
            std::cerr << "update Ras Information error\n";
        }
    }
    catch (const std::exception& e)
    {
        std::cerr << "call update Ras Information error: " << e.what() << "\n";
    }

    return 0;
}

// Correctable or Uncorrectable error count statistics
int handleSumValue(uint64_t& originalValue, uint64_t& newValue)
{
    originalValue += newValue;
    return 0;
}

// Update the memory physical address and number when the last UE or CE is used
int handleSumValue(std::tuple<uint64_t, uint64_t>& originalValue,
                   std::tuple<uint64_t, uint64_t>& newValue)
{
    originalValue = newValue;
    return 0;
}

// Error statistics on PCIe root nodes
int handleSumValue(std::map<uint16_t, std::vector<uint8_t>>& originalValue,
                   std::map<uint16_t, std::vector<uint8_t>>& newValue)
{
    for (auto& [key, vec] : newValue)
    {
        if (originalValue.count(key) > 0)
        {
            auto singleSubPair = originalValue.find(key);
            struct PCIeErrorInfo* pOriginalPCIeRasValue =
                reinterpret_cast<PCIeErrorInfo*>(singleSubPair->second.data());
            struct PCIeErrorInfo* pNewPCIeRasValue =
                reinterpret_cast<PCIeErrorInfo*>(vec.data());

            if ((pOriginalPCIeRasValue->deviceID !=
                 pNewPCIeRasValue->deviceID) ||
                (pOriginalPCIeRasValue->vendorID != pNewPCIeRasValue->vendorID))
            {
                // The device in the PCIE slot is changed.
                // The original value should be discarded and overwritten.
                originalValue[key] = vec;
            }
            else
            {
                pOriginalPCIeRasValue->portType = pNewPCIeRasValue->portType;
                pOriginalPCIeRasValue->cePCIeErrorType |=
                    pNewPCIeRasValue->cePCIeErrorType;
                pOriginalPCIeRasValue->uePCIeErrorType |=
                    pNewPCIeRasValue->uePCIeErrorType;
                pOriginalPCIeRasValue->ruePCIeErrorType |=
                    pNewPCIeRasValue->ruePCIeErrorType;
                pOriginalPCIeRasValue->ceCount += pNewPCIeRasValue->ceCount;
                pOriginalPCIeRasValue->ueCount += pNewPCIeRasValue->ueCount;
            }
        }
        else
        {
            originalValue.emplace(key, vec);
        }
    }
    return 0;
}

template <typename T>
int handleRasValue(rasValue& vOriginalMapValue, rasValue& vNewMapValue)
{
    auto& originalMapValue = std::get<T>(vOriginalMapValue);
    auto& newMapValue = std::get<T>(vNewMapValue);

    for (auto& newPairValue : newMapValue)
    {
        auto singlePair = originalMapValue.find(newPairValue.first);
        if (singlePair != originalMapValue.end())
        {
            handleSumValue(singlePair->second, newPairValue.second);
        }
        else
        {
            originalMapValue.insert(newPairValue);
        }
    }

    return 0;
}

int updateStatisticsValue(uint8_t sensorType,
                          std::map<std::string, rasValue>& mapUpdateValue)
{
    std::map<std::string, rasValue> rasProperties;
    getRasInformationProperties(ipmiSensorPathMap.at(sensorType).c_str(),
                                ipmiSensorIntfMap.at(sensorType).c_str(),
                                rasProperties);
    if (rasProperties.empty())
    {
        return -1;
    }

    for (auto& propertyMap : mapUpdateValue)
    {
        auto originalValue = rasProperties.find(propertyMap.first);
        if (originalValue != rasProperties.end())
        {
            auto indexRasType = propertyMap.second.index();
            switch (indexRasType)
            {
                case 0: // CeUeAddressType
                    handleRasValue<CeUeAddressType>(originalValue->second,
                                                    propertyMap.second);
                    break;
                case 1: // CeUeCountType
                    handleRasValue<CeUeCountType>(originalValue->second,
                                                  propertyMap.second);
                    break;
                case 2: // PCIeRootInfoType
                    handleRasValue<PCIeRootInfoType>(originalValue->second,
                                                     propertyMap.second);
                    break;
            }
            setRasInformationProperties(
                ipmiSensorPathMap.at(sensorType).c_str(),
                ipmiSensorIntfMap.at(sensorType).c_str(),
                propertyMap.first.c_str(), originalValue->second);
        }
        else
        {
            std::cerr << "Failed to update CPER data. [Property:"
                      << propertyMap.first << "] not found in [Path:"
                      << ipmiSensorPathMap.at(sensorType).c_str()
                      << "] [Interface:"
                      << ipmiSensorIntfMap.at(sensorType).c_str() << "]"
                      << std::endl;
        }
    }
    return 0;
}

int parseArmProcessorSingelErrorInfo(std::string& primaryLogId,
                                     CperParseData& cperInfo)
{
    OemHeader* header = &cperInfo.header;
    ArmProcessorSection* processor = &cperInfo.section.processor;
    ErrorInfo* error_info = &processor->first_error_info;

    // BMC time is used when CPER time is not available
    std::string cperTimestamp =
        strlen(header->timestamp_string)
            ? header->timestamp_string
            : primaryLogId.substr(0, primaryLogId.find("@") - 1);

    // RAS statistical Args
    uint64_t ceCount = 0;
    uint64_t ueCount = 0;

    std::string moduleTypeString("Processor");
    if (processor->validation_bits & DIE_ID_VALID)
    {
        moduleTypeString += " Die " + std::to_string(processor->die_id);
    }

    // Redfish Log Args
    std::string redfishSeverity(redfishSeverityMap.at(header->severity));
    std::string redfishMessage(fmt::format("{} occurred {} event.",
                                           moduleTypeString,
                                           rasSeverityMap.at(header->severity))
                                   .c_str());
    std::string redfishMessageArgs;
    std::string errorType;
    std::string errorSubtype;

    if (error_info->validation_bits & ERROR_INFO_CORRECTED_VAILD)
    {
        error_info->corrected ? ++ceCount : ++ueCount;
    }

    if (error_info->validation_bits & ERROR_INFO_TYPE_VALID)
    {
        std::string strDefault("Unknown Processor ErrorType(" +
                               std::to_string(error_info->type) + ")");
        errorType =
            get_cper_string(error_info->type, ARM_ERROR_INFO_ENTRY_INFO_TYPES,
                            strDefault.c_str());
    }

    if (error_info->validation_bits & ERROR_INFO_TRANSACTION_VAILD)
    {
        std::string strDefault("Unknown Transaction Type(" +
                               std::to_string(error_info->transaction) + ")");
        errorSubtype +=
            get_cper_string(error_info->transaction,
                            ARM_ERROR_TRANSACTION_TYPES, strDefault.c_str());
    }

    if (error_info->validation_bits & ERROR_INFO_OPERATION_VAILD)
    {
        std::string strDefault("Unknown Operation Type(" +
                               std::to_string(error_info->operation) + ")");
        if (errorSubtype.size())
        {
            errorSubtype += ",";
        }
        errorSubtype += get_cper_string(error_info->operation,
                                        ARM_CACHE_AND_BUS_OPERATION_TYPES,
                                        strDefault.c_str());
    }

    if (errorType.size())
    {
        redfishMessageArgs = "[" + errorType + "]";
        if (errorSubtype.size())
        {
            redfishMessageArgs += ": " + errorSubtype;
        }
    }

    addRedfishLog(primaryLogId, redfishMessage, redfishMessageArgs,
                  redfishSeverity, cperTimestamp);

    // Construct the ipmi sel field
    // and generate sel events
    std::vector<uint8_t> eventData(3, 0);
    uint8_t ipmiSeverity = ipmiSelSeverityMap.at(header->severity);

    eventData[0] = 0xA0 | (ipmiSeverity & 0x0f);

    if (processor->validation_bits &
        (ERROR_INFO_TYPE_VALID | ERROR_INFO_TRANSACTION_VAILD))
    {
        eventData[1] |= (error_info->type & 0xf); // Processor Error Type
        eventData[1] |= (error_info->transaction & 0x3) << 4;
    }
    else
    {
        eventData[0] &= ~(0x3 << 6); // unspecified eventData byte 2
    }

    if (processor->validation_bits & DIE_ID_VALID)
    {
        eventData[2] = processor->die_id; // Processor Die ID
    }
    else
    {
        eventData[0] &= ~(0x3 << 4); // unspecified eventData byte 3
    }
    addIpmiSelLog(ProcessorInventoryPath, eventData);

    // Update RAS statistics Data
    if (processor->validation_bits & DIE_ID_VALID)
    {
        std::map<std::string, rasValue> updateValue;
        if (ceCount != 0)
        {
            CeUeCountType ce{{processor->die_id, ceCount}};
            updateValue.emplace("ceCount", ce);
        }

        if (ueCount != 0)
        {
            CeUeCountType ue{{processor->die_id, ueCount}};
            updateValue.emplace("ueCount", ue);
        }

        if (!updateValue.empty())
        {
            updateStatisticsValue(PROCESSOR, updateValue);
        }
    }
    return 0;
}

int parsePlatformMemoryEvent(std::string& primaryLogId, CperParseData& cperInfo)
{
    OemHeader* header = &cperInfo.header;
    MemorySection* memory = &cperInfo.section.memory;

    // BMC time is used when CPER time is not available
    std::string cperTimestamp =
        strlen(header->timestamp_string)
            ? header->timestamp_string
            : primaryLogId.substr(0, primaryLogId.find("@") - 1);

    // IPMI SEL Args
    uint8_t memoryChannel = 0xff;

    std::string moduleTypeString("Memory X");
    std::string errorType("ErrorType: Unknown");
    std::string errorSubtype;

    if (memory->validation_bits & MODULE_SMBIOS_HANDLE_VAILD)
    {
        const struct OemMemoryInfo* pMemoryInfo =
            getMemoryInfoByHandle(memory->module_smbios_handle);
        if (pMemoryInfo)
        {
            moduleTypeString = "Memory " + std::string(pMemoryInfo->deviceLocator);
            memoryChannel = pMemoryInfo->channel;
        }
        else
        {
            moduleTypeString = "Memory Smbios Handle(" +
                               std::to_string(memory->module_smbios_handle) +
                               ")";
        }
    }

    if (memory->validation_bits & PHYSICAL_ADDRESS_VAILD)
    {
        errorSubtype = fmt::format("#physical address is 0x{:x} ",
                                   memory->physical_address)
                           .c_str();

        // When there is no smbios handle,
        // use physical_address to get the memory channel.
        if (!(memory->validation_bits & MODULE_SMBIOS_HANDLE_VAILD))
        {
            constexpr uint32_t GB = 1024 * 1024 * 1024; // 1GB
            uint8_t dieId = memory->physical_address / GB / dieMemorySize;
            moduleTypeString =
                fmt::format("Die {} Memory Channel(1 or 2)", dieId).c_str();
        }

        // Update RAS statistics Data
        if (memoryChannel != 0xff)
        {
            CeUeCountType Count{{memoryChannel, 1}};
            CeUeAddressType Address{
                {memoryChannel, {memory->physical_address, 1}}};
            std::map<std::string, rasValue> updateValue;
            if (header->severity == CORRECTED)
            {
                updateValue.emplace("ceCount", Count);
                updateValue.emplace("cePhysicalAddress", Address);
            }
            else if (header->severity == RECOVERABLE ||
                     header->severity == FATAL)
            {
                updateValue.emplace("ueCount", Count);
                updateValue.emplace("uePhysicalAddress", Address);
            }
            if (!updateValue.empty())
            {
                updateStatisticsValue(MEMORY, updateValue);
            }
        }
    }

    // Redfish Log Args
    std::string redfishSeverity(redfishSeverityMap.at(header->severity));
    std::string redfishMessage(fmt::format("{} occurred {} event.",
                                           moduleTypeString,
                                           rasSeverityMap.at(header->severity))
                                   .c_str());

    if (memory->validation_bits & ERROR_TYPE_VALID)
    {
        errorType = get_cper_string(
            memory->error_type, MEMORY_ERROR_TYPES,
            fmt::format("{}({})", errorType, memory->error_type).c_str());
    }

    std::string messageArgs(errorType + errorSubtype);

    addRedfishLog(primaryLogId, redfishMessage, messageArgs, redfishSeverity,
                  cperTimestamp);

    // Generate data in IPMI SEL format
    std::vector<uint8_t> eventData(3, 0);
    uint8_t ipmiSeverity = ipmiSelSeverityMap.at(header->severity);

    eventData[0] = 0xA0 | (ipmiSeverity & 0x0f);

    if (memory->validation_bits & ERROR_TYPE_VALID)
    {
        eventData[1] = memory->error_type & 0x0f; // Memory Error Type
    }
    else
    {
        eventData[0] &= ~(0x3 << 6); // unspecified eventData byte 2
    }

    if (memoryChannel != 0xff)
    {
        eventData[2] = memoryChannel; // Memory Error Channel
    }
    else
    {
        eventData[0] &= ~(0x3 << 4); // unspecified eventData byte 3
    }

    addIpmiSelLog(MemoryInventoryPath, eventData);

    return 0;
}

const char* parsePCIeAerInfo(uint8_t severity, aerCapabilityRegs* aerData,
                             std::vector<uint8_t>& infoValue)
{
    infoValue.resize(sizeof(struct PCIeErrorInfo));

    struct PCIeErrorInfo* pPCIeRasValue =
        reinterpret_cast<PCIeErrorInfo*>(infoValue.data());

    uint32_t aerStatus = 0;
    switch (severity)
    {
        case CORRECTED:
            aerStatus = aerData->cor_status & ~aerData->cor_mask;
            pPCIeRasValue->cePCIeErrorType = aerStatus;
            pPCIeRasValue->ceCount = __builtin_popcount(aerStatus);
            break;
        case RECOVERABLE:
            aerStatus = aerData->uncor_status & ~aerData->uncor_mask;
            pPCIeRasValue->ruePCIeErrorType = aerStatus;
            pPCIeRasValue->ueCount = __builtin_popcount(aerStatus);
            break;
        case FATAL:
            aerStatus = aerData->uncor_status & ~aerData->uncor_mask &
                        aerData->uncor_severity;
            pPCIeRasValue->uePCIeErrorType = aerStatus;
            pPCIeRasValue->ueCount = __builtin_popcount(aerStatus);
            break;
    }

    return getAerErrorTypeString(severity, aerStatus);
}

int parsePCIeEvent(std::string& primaryLogId, CperParseData& cperInfo)
{
    OemHeader* header = &cperInfo.header;
    PcieSection* pcie = &cperInfo.section.pcie;

    // BMC time is used when CPER time is not available
    std::string cperTimestamp =
        strlen(header->timestamp_string)
            ? header->timestamp_string
            : primaryLogId.substr(0, primaryLogId.find("@") - 1);

    // Redfish Log Args
    std::string redfishMessage;
    std::string portType, pcieInfo, pcieAerInfo;
    std::string redfishSeverity(redfishSeverityMap.at(header->severity));

    if (pcie->validation_bits & PORT_TYPE_VALID)
    {
        portType = "PortType: " +
                   std::string(get_cper_string(
                       pcie->port_type, PCIE_ERROR_PORT_TYPES,
                       fmt::format("Unknown({})", pcie->port_type).c_str()));
    }

    if (pcie->validation_bits & DEVICE_INFO_VAILD)
    {
        redfishMessage = fmt::format("Die {} PCIE Root {} occurred {} event.",
                                     pcie->device_info.die_id,
                                     pcie->device_info.device_number,
                                     rasSeverityMap.at(header->severity))
                             .c_str();

        pcieInfo =
            fmt::format(
                "#PCIe Info:[{:04x}:{:02x}:{:02x}.{:01d}]"
                "VendorID:{:04x} DeviceID:{:04x} slotId:{:d}.",
                pcie->device_info.die_id, pcie->device_info.bus_number,
                pcie->device_info.device_number,
                pcie->device_info.function_number, pcie->device_info.vendor_id,
                pcie->device_info.device_id, pcie->device_info.slot_number)
                .c_str();
    }

    std::vector<uint8_t> PCIeData;
    if (pcie->validation_bits & AER_INFO_VAILD)
    {
        pcieAerInfo = fmt::format("#PCIeAerInfo:{}",
                                  parsePCIeAerInfo(header->severity,
                                                   &pcie->aer_info, PCIeData))
                          .c_str();
    }

    if (!redfishMessage.size())
    {
        redfishMessage = fmt::format("PCIE Root X occurred {} event.",
                                     rasSeverityMap.at(header->severity))
                             .c_str();
    }
    std::string messageArgs(portType + pcieInfo + pcieAerInfo);

    addRedfishLog(primaryLogId, redfishMessage, messageArgs, redfishSeverity,
                  cperTimestamp);

    // Update RAS statistics Data
    if (pcie->validation_bits & AER_INFO_VAILD &&
        pcie->validation_bits & DEVICE_INFO_VAILD)
    {
        std::map<std::string, rasValue> updateValue;
        struct PCIeErrorInfo* pPCIeRasValue =
            reinterpret_cast<PCIeErrorInfo*>(PCIeData.data());

        pPCIeRasValue->portType =
            (pcie->validation_bits & AER_INFO_VAILD) ? pcie->port_type : 0xff;
        pPCIeRasValue->vendorID = pcie->device_info.vendor_id;
        pPCIeRasValue->deviceID = pcie->device_info.device_id;
        pPCIeRasValue->slotNumber = pcie->device_info.slot_number;

        PCIeRootInfoType updatePCIeValue{
            {pcie->device_info.die_id,
             {{pcie->device_info.bus_number, PCIeData}}}};

        updateValue.emplace("pcieRootInfo", updatePCIeValue);

        updateStatisticsValue(PCIE, updateValue);
    }

    // Generate data in IPMI SEL format
    std::vector<uint8_t> eventData(3, 0);
    eventData[0] = ipmiSelSeverityMap.at(header->severity) & 0x0f;
    if (pcie->validation_bits & DEVICE_INFO_VAILD)
    {
        // OEM code in byte2 and byte3
        eventData[0] |= 0xA0;
        // Die Number
        eventData[1] = pcie->device_info.die_id;
        // PCI Root Number
        eventData[2] = pcie->device_info.device_number;
    }

    addIpmiSelLog(PCIeIventoryPath, eventData);

    return 0;
}

int parsePhytiumErrorEvent(std::string& primaryLogId, CperParseData& cperInfo)
{
    OemHeader* header = &cperInfo.header;
    PhytiumSection* phytium = &cperInfo.section.phytium;

    // BMC time is used when CPER time is not available
    std::string cperTimestamp =
        strlen(header->timestamp_string)
            ? header->timestamp_string
            : primaryLogId.substr(0, primaryLogId.find("@") - 1);

    // Redfish Log Args
    std::string errorCode(
        fmt::format("Error Code: 0x{:x}", phytium->error_code).c_str());
    std::string errorStatus(
        fmt::format("#Error Status: 0x{:x}", phytium->error_status).c_str());
    std::string address;
    std::string moduleType;

    switch (phytium->module_type)
    {
        case PHYTIUM_PROCESSOR_MODULE:
        {
            moduleType = "Processor Die " + std::to_string(phytium->die_id);

            // Construct the ipmi sel field
            // and generate sel events
            std::vector<uint8_t> eventData(3, 0);
            eventData[0] =
                0xA0 | (ipmiSelSeverityMap.at(header->severity) & 0x0f);
            eventData[1] = phytium->error_code | (0x1 << 7);
            eventData[2] = phytium->die_id; // Processor ID
            addIpmiSelLog(ProcessorInventoryPath, eventData);

            // Update IPMI Discrete Sensor Severity
            updateDiscreteSensorSeverity(PROCESSOR, header->severity);

            // Update RAS statistics Data
            std::map<std::string, rasValue> updateValue;
            CeUeCountType count{{phytium->die_id, 1}};
            updateValue.emplace(
                (header->severity <= FATAL) ? "ueCount" : "ceCount", count);
            updateStatisticsValue(PROCESSOR, updateValue);
            break;
        }
        case PHYTIUM_DDR_MODULE:
        {
            uint8_t errorChannel =
                phytium->die_id * dieMemoryCount + phytium->module_id;

            // The memory module described by
            // the SMBIOS is displayed preferentially.
            // If the SMBIOS channel ID is different from the CPER channel ID,
            // the SMBIOS information is not used.
            if (memoryTotalNumber > errorChannel &&
                oemMemoryInfoMap[errorChannel].channel == errorChannel)
            {
                moduleType =
                    "Memory " +
                    std::string(oemMemoryInfoMap[errorChannel].deviceLocator);
            }
            else
            {
                uint8_t channelSize = dieMemoryInfoMap.size();
                uint8_t dimmId =
                    dieMemoryInfoMap.at(errorChannel % channelSize) +
                    (errorChannel / channelSize * channelSize);
                moduleType = "Memory DIMM " + std::to_string(dimmId);
            }

            address = fmt::format("#physical address is 0x{:x} ",
                                  phytium->physical_address)
                          .c_str();

            // Update RAS statistics Data
            CeUeCountType Count{{errorChannel, 1}};
            CeUeAddressType Address{
                {errorChannel, {phytium->physical_address, 1}}};

            std::map<std::string, rasValue> updateValue;
            std::string propertyCount =
                (header->severity <= FATAL) ? "ueCount" : "ceCount";
            std::string propertyAddress = (header->severity <= FATAL)
                                              ? "uePhysicalAddress"
                                              : "cePhysicalAddress";

            updateValue.emplace(propertyCount, Count);
            updateValue.emplace(propertyAddress, Address);
            updateStatisticsValue(MEMORY, updateValue);

            // Construct the ipmi sel field
            // and generate sel events
            std::vector<uint8_t> eventData(3, 0);
            eventData[0] =
                0xA0 | (ipmiSelSeverityMap.at(header->severity) & 0x0f);
            eventData[1] = phytium->error_code | (0x1 << 7);
            eventData[2] = errorChannel; // MemoryChannelId
            addIpmiSelLog(MemoryInventoryPath, eventData);

            // Update IPMI Discrete Sensor Severity
            updateDiscreteSensorSeverity(MEMORY, header->severity);
            break;
        }
        case PHYTIUM_PCIE_MODULE:
        {
            moduleType = fmt::format("Processor Die {} PCIE Controller {}",
                                     phytium->die_id, phytium->module_id)
                             .c_str();

            // Construct the ipmi sel field
            // and generate sel events
            std::vector<uint8_t> eventData(3, 0);
            eventData[0] =
                0xA0 | (ipmiSelSeverityMap.at(header->severity) & 0x0f);
            eventData[1] = phytium->error_code | (0x1 << 7);
            eventData[2] = phytium->die_id; // Processor ID
            addIpmiSelLog(PCIeIventoryPath, eventData);

            // Update IPMI Discrete Sensor Severity
            updateDiscreteSensorSeverity(PCIE, header->severity);
            break;
        }
        default:
        {
            moduleType = fmt::format("Processor Die {} {} {}", phytium->die_id,
                                     get_cper_string(phytium->module_type,
                                                     PHYTIUM_MODULE_TYPES,
                                                     "Unknown Module"),
                                     phytium->module_id)
                             .c_str();
            break;
        }
    }

    std::string redfishMessage(fmt::format("{} occurred {} event.", moduleType,
                                           rasSeverityMap.at(header->severity))
                                   .c_str());
    std::string redfishSeverity(redfishSeverityMap.at(header->severity));
    std::string messageArgs(errorCode + errorStatus + address);

    addRedfishLog(primaryLogId, redfishMessage, messageArgs, redfishSeverity,
                  cperTimestamp);

    return 0;
}

static int createFile(std::string fileSavePath, const uint8_t* data,
                      size_t size)
{
    fs::path filePath(fileSavePath);

    if (!fs::is_directory(filePath.parent_path()))
    {
        if (fs::exists(filePath.parent_path()))
        {
            fs::remove_all(filePath.parent_path());
        }
        fs::create_directories(filePath.parent_path());
    }

    std::ofstream out(filePath, std::ofstream::binary | std::ofstream::app);
    if (!out.is_open())
    {
        std::cerr << "Can not open ofstream for CPER binary file" << std::endl;
        return -1;
    }

    out.write(reinterpret_cast<const char*>(data), size);
    out.close();

    return 0;
}

static int readFile(std::string tmpPathFile, std::vector<uint8_t>& fileBuf)
{
    std::ifstream ifs;
    ifs.open(tmpPathFile, std::ios_base::binary);
    if (!ifs.is_open())
    {
        std::cerr << "Can not open ifstream for [" << tmpPathFile
                  << "] binary file\n";
        return -1;
    }

    ifs.seekg(0, std::ios::end);
    fileBuf.resize(ifs.tellg());
    ifs.seekg(0);
    ifs.read(reinterpret_cast<char*>(fileBuf.data()), fileBuf.size());
    ifs.close();

    return 0;
}

bool isOldCperFile(CperParseData& cperInfo, std::string& cperSavePath,
                   uint16_t sectionIndex)
{
#define SAVE_UNIQUE_CPER_FILE_PATH "/tmp/unique_cper/"

    std::string lastCperFile;
    switch (cperInfo.header.section_type)
    {
        case CPER_ARM_PROCESSOR:
        {
            ArmProcessorSection* processor = &cperInfo.section.processor;
            lastCperFile =
                fmt::format("Arm_Processor_Die_{}", processor->die_id).c_str();
            break;
        }
        case CPER_MEMORY:
        {
            MemorySection* memory = &cperInfo.section.memory;
            if (memory->validation_bits & MODULE_SMBIOS_HANDLE_VAILD)
            {
                lastCperFile = fmt::format("Memory_SmbiosHandle_{}",
                                           memory->module_smbios_handle)
                                   .c_str();
            }
            else if (memory->validation_bits & PHYSICAL_ADDRESS_VAILD)
            {
                uint8_t dieId = memory->physical_address / dieMemorySize;
                lastCperFile =
                    fmt::format("Die_{}_Memory_Channel", dieId)
                        .c_str();
            }
            else
            {
                lastCperFile = "Memory_CPER";
            }
            break;
        }
        case CPER_PCIE:
        {
            PcieSection* pcie = &cperInfo.section.pcie;
            if (pcie->validation_bits & DEVICE_INFO_VAILD)
            {
                lastCperFile =
                    fmt::format("Pcie_Die{}_Port{}", pcie->device_info.die_id,
                                pcie->device_info.device_id)
                        .c_str();
            }
            else
            {
                lastCperFile = "PCIe_CPER";
            }
            break;
        }
        case CPER_PHYTIUM:
        {
            PhytiumSection* phytium = &cperInfo.section.phytium;
            lastCperFile =
                fmt::format("Phytium_Type{}_Die{}_{}", phytium->module_type,
                            phytium->die_id, phytium->module_id)
                    .c_str();
            break;
        }
        case CPER_UNKNOWN:
        default:
            return false;
    }

    fs::path uniqueCperDir(SAVE_UNIQUE_CPER_FILE_PATH);
    if (!fs::is_directory(uniqueCperDir))
    {
        if (fs::exists(uniqueCperDir))
        {
            fs::remove_all(uniqueCperDir);
        }
        fs::create_directories(uniqueCperDir);
    }

    fs::path cmpSymlink(uniqueCperDir / lastCperFile);

    // Both links and targets are present.
    if (fs::exists(cmpSymlink) && fs::is_symlink(cmpSymlink))
    {
        auto targetPath = fs::read_symlink(cmpSymlink);

        // Get a handle for the old log file.
        FILE* fpCper = fopen(targetPath.c_str(), "rb");
        if (fpCper == nullptr)
        {
            return false;
        }
        CperParseData oldCperInfo;
        if (parse_cper_event(fpCper, &oldCperInfo, sectionIndex))
        {
            std::cerr
                << fmt::format(
                       "[CheckOldCperFile]Parse CPER Section Index {} Fail!",
                       sectionIndex)
                       .c_str()
                << std::endl;
            fclose(fpCper);
            return false;
        }
        fclose(fpCper);

        // Same module event, compare content.
        if (calCrc16(reinterpret_cast<uint8_t*>(&oldCperInfo),
                     sizeof(CperParseData)) ==
            calCrc16(reinterpret_cast<uint8_t*>(&cperInfo),
                     sizeof(CperParseData)))
        {
            // Duplicate CPER logs
            return true;
        }
    }

    // The new log
    fs::remove_all(cmpSymlink);
    fs::create_symlink(fs::path(cperSavePath), cmpSymlink);

    return false;
}

int parseCperRecord(std::vector<uint8_t>& cperBuf, uint8_t parseArgs)
{
    std::string cperFileId = getUniqueEntryID("");
    std::string cperSavePath(std::string(CPER_LOG_DIR) + cperFileId);

    // Get a handle for the log file.
    FILE* fpCper = fmemopen(cperBuf.data(), cperBuf.size(), "rb");
    if (fpCper == nullptr)
    {
        createFile(cperSavePath, cperBuf.data(), cperBuf.size());
        std::cerr
            << "Could not open provided CPER Buf,FILE handle returned null."
            << std::endl;
        return -1;
    }

    // Parse Cper
    CperParseData cperInfo;
    uint16_t sectionIndex = 0;
    uint16_t duplicateCperCount = 0;
    do
    {
        fseek(fpCper, 0, SEEK_SET);
        int ret = parse_cper_event(fpCper, &cperInfo, sectionIndex);
        if (ret == INVALIDE_CPER_DATA)
        {
            std::cerr << "Invalid CPER Event" << std::endl;
            cperSavePath += "_Invalid";
            break;
        }
        else if (ret != PARSE_SUCCESS)
        {
            std::cerr << "Parse CPER Section Index " << sectionIndex
                      << " Fail! (ret = " << ret << ")\n";
            ++sectionIndex;
            continue;
        }

        if (parseArgs == COMPARE_AND_LABEL_NEW_CPER &&
            isOldCperFile(cperInfo, cperSavePath, sectionIndex))
        {
            std::cerr << fmt::format("[{}]CPER Section Index {} isOldCperFile!",
                                     cperFileId, sectionIndex)
                             .c_str()
                      << std::endl;
            ++sectionIndex;
            ++duplicateCperCount;
            continue;
        }

        std::string primaryLogId =
            cperFileId + "@" + std::to_string(sectionIndex);
        switch (cperInfo.header.section_type)
        {
            case CPER_ARM_PROCESSOR:
            {
                int errinfo_count = cperInfo.section.processor.errinfo_count;
                ErrorInfo* error_infos = nullptr;
                if (errinfo_count > 1)
                {
                    error_infos = new ErrorInfo[errinfo_count];
                    if (error_infos != nullptr)
                    {
                        fseek(fpCper, 0, SEEK_SET);
                        errinfo_count = get_arm_processor_errorinfos(
                            fpCper, sectionIndex, error_infos, errinfo_count);
                        for (int i = 0; i < errinfo_count; i++)
                        {
                            memcpy((uint8_t*)&cperInfo.section.processor
                                       .first_error_info,
                                   (uint8_t*)&error_infos[i],
                                   sizeof(ErrorInfo));
                            parseArmProcessorSingelErrorInfo(primaryLogId,
                                                             cperInfo);
                        }
                        delete error_infos;
                    }
                }
                else
                {
                    parseArmProcessorSingelErrorInfo(primaryLogId, cperInfo);
                }
                updateDiscreteSensorSeverity(PROCESSOR,
                                             cperInfo.header.severity);
                break;
            }
            case CPER_MEMORY:
                parsePlatformMemoryEvent(primaryLogId, cperInfo);
                updateDiscreteSensorSeverity(MEMORY, cperInfo.header.severity);
                break;
            case CPER_PCIE:
                parsePCIeEvent(primaryLogId, cperInfo);
                updateDiscreteSensorSeverity(PCIE, cperInfo.header.severity);
                break;
            case CPER_PHYTIUM:
                parsePhytiumErrorEvent(primaryLogId, cperInfo);
                break;
            case CPER_UNKNOWN:
            default:
                std::string redfishMessage("Unknown Cper Event");
                std::string redfishMessageArgs("Unknown");
                std::string redfishSeverity(
                    redfishSeverityMap.at(cperInfo.header.severity));
                std::string cperTimestamp =
                    strlen(cperInfo.header.timestamp_string)
                        ? cperInfo.header.timestamp_string
                        : primaryLogId.substr(0, primaryLogId.find("@") - 1);
                addRedfishLog(primaryLogId, redfishMessage, redfishMessageArgs,
                              redfishSeverity, cperTimestamp);
                break;
        }

        std::cout << "Generate CPER events: " << primaryLogId << std::endl;

        ++sectionIndex;

    } while (sectionIndex < cperInfo.header.section_count);

    fclose(fpCper);

    if(duplicateCperCount != sectionIndex)
    {
        createFile(cperSavePath, cperBuf.data(), cperBuf.size());
    }

    return 0;
}

int parseProceeosrCoreBistMap(std::vector<uint8_t>& bistMapBuf,
                              uint8_t parseArgs)
{
    constexpr uint8_t s5000cDieCoreCount = 16;
    constexpr uint8_t singleMapSize = 8;

    std::cerr << "Start parseProceeosrCoreBistMap" << std::endl;
    if (bistMapBuf.size() < singleMapSize)
    {
        std::cerr << "Invalid Proceeosr Core Bist Map Data" << std::endl;
        return -1;
    }

    uint64_t mapCount = *(reinterpret_cast<uint64_t*>(bistMapBuf.data()));
    if (bistMapBuf.size() != (2 * singleMapSize * mapCount + sizeof(mapCount)))
    {
        std::cerr << "Invalid Core Bist Map Count:" << mapCount << std::endl;
        return -1;
    }

    uint64_t* pCoerMap =
        reinterpret_cast<uint64_t*>(&bistMapBuf[sizeof(mapCount)]);
    uint64_t* pBistMap = reinterpret_cast<uint64_t*>(
        &bistMapBuf[singleMapSize * mapCount + sizeof(mapCount)]);

    for (uint64_t dieId = 0; dieId < mapCount; dieId++)
    {
        uint64_t coreBistResults = pCoerMap[dieId] ^ pBistMap[dieId];
        if (coreBistResults)
        {
            for (uint8_t coreId = 0; coreId < s5000cDieCoreCount; coreId++)
            {
                if ((coreBistResults >> coreId) & 0x1)
                {
                    std::cerr
                        << fmt::format("Proceeosr Die [{}]: Core {} Bist Fail!",
                                       dieId, coreId)
                               .c_str()
                        << std::endl;
                }
            }
        }
        else
        {
            std::cerr << fmt::format(
                             "Proceeosr Die [{}]: All Core Bist Success!",
                             dieId)
                             .c_str()
                      << std::endl;
        }
    }
    return 0;
}

ipmi::RspType<std::vector<uint8_t>> oemReceivePacketsHandle(
    const std::vector<uint8_t>& reqData, std::string fileSavePath,
    int (*parseHandle)(std::vector<uint8_t>&, uint8_t), uint8_t parseArgs)
{
    if (reqData[PACKET_ID_OFFSET] == FIRST_PACKET)
    {
        if (fs::exists(fileSavePath))
        {
            fs::remove_all(fileSavePath);
        }

        if (createFile(fileSavePath, &reqData[PACKET_ID_OFFSET + 1],
                       reqData.size() - PACKET_ID_OFFSET - 1) < 0)
        {
            std::cerr << "Create Bist Map Temp File Fail,Packet ID = "
                      << reqData[PACKET_ID_OFFSET] << std::endl;
            return ipmi::responseUnspecifiedError();
        }
    }
    else if (reqData[PACKET_ID_OFFSET] == LAST_PACKET)
    {
        if (createFile(fileSavePath, &reqData[PACKET_ID_OFFSET + 1],
                       reqData.size() - PACKET_ID_OFFSET - 1) < 0)
        {
            std::cerr << "Create Bist Map Temp File Fail: Packet ID = "
                      << reqData[PACKET_ID_OFFSET] << std::endl;
            return ipmi::responseUnspecifiedError();
        }

        std::vector<uint8_t> parseBuf;
        if (readFile(fileSavePath, parseBuf) < 0)
        {
            std::cerr << "Read Bist Map File Fail" << std::endl;
            return ipmi::responseUnspecifiedError();
        }
        parseHandle(parseBuf, parseArgs);
    }
    else
    {
        if (createFile(fileSavePath, &reqData[PACKET_ID_OFFSET + 1],
                       reqData.size() - PACKET_ID_OFFSET - 1) < 0)
        {
            std::cerr << "create Cper Temp File Fail: Packet ID = "
                      << reqData[PACKET_ID_OFFSET] << std::endl;
            return ipmi::responseUnspecifiedError();
        }
    }
    uint16_t crc16 = calCrc16(reqData.data(), reqData.size());
    return ipmi::responseSuccess(
        std::vector<uint8_t>({static_cast<uint8_t>(crc16 & 0xff),
                              static_cast<uint8_t>(crc16 >> 8)}));
}

/* Add Common Platform Error Record Command */
ipmi::RspType<std::vector<uint8_t>>
    ipmiAddCommonPlatformErrorRecord(const std::vector<uint8_t>& reqData)
{
    printBuf("CPER Data:", reqData);
    return oemReceivePacketsHandle(reqData, TEMP_FAULT_LOG_PATH,
                                   parseCperRecord, COMPARE_AND_LABEL_NEW_CPER);
}

/* Add Proceeosr Core Bist Map Command*/
ipmi::RspType<std::vector<uint8_t>>
    ipmiAddProceeosrCoreBistMap(const std::vector<uint8_t>& reqData)
{
    printBuf("Processor Core Bist Map Data:", reqData);
    return oemReceivePacketsHandle(reqData, TEMP_CORE_BIST_MAP_PATH,
                                   parseProceeosrCoreBistMap, NO_PARAMETERS);
}

/* Read Platform Error Record Command*/
ipmi::RspType<std::vector<uint8_t>> ipmiReadCommonPlatformErrorRecord(
    ipmi::Context::ptr ctx, const uint8_t channelIdx, const uint8_t rasType)
{
    // Check if it is an IPMB channel and if the type meets the range.
    if (channelIdx & ipmbChannelMask)
    {
        std::cerr << "The requested data format is incorrect." << std::endl;
        ipmi::responseInvalidFieldRequest();
    }

    std::vector<uint8_t> tmpBuf(0);
    uint8_t retryCount = 0;

    for (int index = 0; index < LAST_PACKET; index++)
    {
        if (retryCount >= ipmbRetriesCount)
        {
            std::cerr << "Retry still failed, exit." << std::endl;
            return ipmi::responseResponseError();
        }

        typedef std::tuple<int, uint8_t, uint8_t, uint8_t, uint8_t,
                           std::vector<uint8_t>>
            ipmbResponse;

        boost::system::error_code ec;
        auto ipmbResData = ctx->bus->yield_method_call<ipmbResponse>(
            ctx->yield, ec, "xyz.openbmc_project.Ipmi.Channel.Ipmb",
            "/xyz/openbmc_project/Ipmi/Channel/Ipmb", "org.openbmc.Ipmb",
            "sendRequest", channelIdx, oemIpmbNetfn, oemIpmbLun, oemIpmbCmd,
            std::vector<uint8_t>({rasType, static_cast<uint8_t>(index)}));
        if (ec)
        {
            std::cerr << "Dbus call execption." << std::endl;
            retryCount++;
            index--;
            continue;
        }

        int status = -1;
        uint8_t netfn = 0, lun = 0, cmd = 0, cc = 0;
        std::vector<uint8_t> receivedData(0);

        std::tie(status, netfn, lun, cmd, cc, receivedData) = ipmbResData;

        // If the status is incorrect or the response data received is
        // incorrect, a resend operation will be performed, and the maximum
        // number of resends will be returned.
        if (status || cc || (receivedData.size() > 0 && 
            receivedData[0] != index && receivedData[0] != LAST_PACKET))
        {
            std::cerr << "Command error, try to resend." << std::endl;
            retryCount++;
            index--;
            continue;
        }

        if((receivedData.size() == 0) || (index == FIRST_PACKET &&
            receivedData.size() == 1 && receivedData[0] == LAST_PACKET))
        {
            // No CPER error detected
            std::cout << "Scp has no CPER errors" << std::endl;
            return ipmi::responseSuccess();
        }

        uint8_t resIndex = receivedData[0];

        retryCount = 0;
        tmpBuf.insert(tmpBuf.end(), receivedData.begin() + 1,
                      receivedData.end());

        if (resIndex == LAST_PACKET)
        {
            parseCperRecord(tmpBuf, COMPARE_AND_LABEL_NEW_CPER);

            return ipmi::responseSuccess();
        }
    }

    return ipmi::responseUnspecifiedError();
}

static void register_ras_functions() __attribute__((constructor));
static void register_ras_functions()
{

    ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnOemSix,
                          oem::cmdAddCommonPlatformErrorRecord,
                          ipmi::Privilege::User,
                          ipmiAddCommonPlatformErrorRecord);

    ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnOemSix,
                          oem::cmdReadCommonPlatformErrorRecord,
                          ipmi::Privilege::Admin,
                          ipmiReadCommonPlatformErrorRecord);

    ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnOemSix,
                          oem::cmdAddProceeosrCoreBistMap,
                          ipmi::Privilege::Admin, ipmiAddProceeosrCoreBistMap);
    return;
}
